#include <cppcms/application.h>  
#include <cppcms/applications_pool.h>  
#include <cppcms/service.h>  
#include <cppcms/http_response.h>  
#include <cppcms/url_dispatcher.h>  
#include <iostream>
#include <cppdb/frontend.h>
#include <ctime>
#include "content.h"
#include "constants.h"

using namespace std;

/**
 * Klasa aplikacji webowej.
 * @param srv
 */
class netspy : public cppcms::application {
private:
    cppdb::session sql;

public:
    /**
     * Konstruktor inicjalizujący routing oraz połączenie z bazą danych.
     * @param srv
     */
    netspy(cppcms::service &srv) : cppcms::application(srv) {
        sql = cppdb::session(string("mysql:database=") + string(DATABASE) + string(";user=") + string(USER) + string(";password=") + string(PASSWORD));
        dispatcher().assign("/service/(\\d+)/comp/(\\d+)", &netspy::history, this, 1, 2);
        dispatcher().assign("/comp/(\\d+)/service/(\\d+)", &netspy::history, this, 2, 1);
        dispatcher().assign("/overview", &netspy::overview, this);
        dispatcher().assign("", &netspy::overview, this);
        dispatcher().assign("/", &netspy::overview, this);
        dispatcher().assign("/comp/(\\d+)", &netspy::overview_comp, this, 1);
        dispatcher().assign("/service/(\\d+)", &netspy::overview_service, this, 1);
        dispatcher().assign("/clients", &netspy::clientsList, this);
        dispatcher().assign("/services", &netspy::servicesList, this);
    }

    virtual ~netspy() {
        sql.close();
    }
    
    void overview() {
        this->overview("");
    }

    void overview_comp(std::string comp) {
        this->overview(std::string("record.client_id=") + comp);
    }

    void overview_service(std::string service) {
        this->overview(std::string("record.service_id=") + service);
    }

    void overview(std::string condition) {
        try {
            std::string temp;
            std::string query = "select * from record "
                    "right join (select max(timestamp) as last, client_id, service_id from record group by client_id, service_id) as refer on (record.timestamp=refer.last and record.client_id=refer.client_id and record.service_id=refer.service_id) "
                    "left join reply on (record.reply_id=reply._id) "
                    "left join service on (record.service_id = service._id) "
                    "left join client on (record.client_id = client._id)";
            if ((&condition) != NULL && strlen(condition.c_str()) > 0) {
                query += " where " + condition;
            }
            cppdb::statement stat = sql << query;
            cppdb::result res = stat.query();
            content::overview o;
            o.size = stat.affected();
            while (res.next()) {
                o.ip.push_back(res.get<std::string > ("ip"));
                o.port.push_back(res.fetch("port", temp) ? temp : "");
                o.timestamp.push_back(res.get<std::string > ("timestamp"));
                o.status.push_back(res.get<std::string > ("message"));
                o.remarks.push_back(res.fetch("remarks", temp) ? temp : "");
                o.service.push_back(res.get<std::string > ("description"));
                o.comp_id.push_back(res.get<std::string > ("client_id"));
                o.service_id.push_back(res.get<std::string > ("service_id"));
            }
            render("overview", o);
        } catch (std::exception const &e) {
            response().out() << e.what() << std::endl;
        }
    }

    void servicesList() {
        try {
            content::services s;
            cppdb::statement stat = sql << "SELECT * from service";
            cppdb::result res = stat.query();

            s.size = stat.affected();
            while (res.next()) {
                s.description.push_back(res.get<std::string > ("description"));
                s.id.push_back(res.get<std::string > ("_id"));
            }
            render("services", s);
        } catch (std::exception const &e) {
            response().out() << "ERROR: " << e.what() << std::endl;
        }
    }

    void clientsList() {
        std::string temp;
        try {
            content::clients c;
            cppdb::statement stat = sql << "SELECT * from client";
            cppdb::result res = stat.query();
            c.size = stat.affected();
            while (res.next()) {
                c.id.push_back(res.get<int>("_id"));
                c.ip.push_back(res.get<std::string > ("ip"));
                c.port.push_back(res.fetch("port", temp) ? temp : "");
                c.remarks.push_back(res.fetch("remarks", temp) ? temp : "");
            }
            render("clients", c);
        } catch (std::exception const &e) {
            response().out() << "ERROR: " << e.what() << std::endl;
        }
    }

    void history(std::string s_id, std::string c_id) {
        try {
            std::string temp;
            std::string query = std::string("select * from record left join reply on (record.reply_id=reply._id) "
                    "left join client on (record.client_id=client._id) left join service on (record.service_id = service._id) where service_id=")
                    + s_id + std::string(" and client_id=") + c_id + std::string(" order by timestamp desc");
            cppdb::statement stat = sql << query;
            cppdb::result res = stat.query();
            content::history h;
            h.size = stat.affected();
            bool first_loop = true;
            while (res.next()) {
                if (first_loop) {
                    h.ip = res.get<std::string > ("ip");
                    h.port = res.fetch("port", temp) ? temp : "";
                    h.remarks = res.fetch("remarks", temp) ? temp : "";
                    h.service = res.get<std::string > ("description");
                    first_loop = false;
                }
                h.status.push_back(res.get<std::string > ("message"));
                h.timestamp.push_back(res.get<std::string > ("timestamp"));
            }
            render("history", h);

        } catch (std::exception const &e) {
            std::cerr << "ERROR: " << e.what() << std::endl;
        }
    }

    void dbtest() {
        response().out() << "We will rock u <br />";
        try {

            cppdb::result res = sql << "SELECT * FROM client";

            while (res.next()) {
                int id = res.get<int>("_id");
                std::string ip = res.get<std::string > ("ip");
                response().out() << id << " " << ip << std::endl;
            }
        } catch (std::exception const &e) {
            response().out() << "ERROR: " << e.what() << std::endl;
        }
    }

};

int main(int argc, char ** argv) {
    try {
        cppcms::service srv(argc, argv);
        srv.applications_pool().mount(cppcms::applications_factory<netspy > ());
        srv.run();
    } catch (std::exception const &e) {
        std::cerr << e.what() << std::endl;
    }
}
